﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Net;
using System.Data;
using System.Text.RegularExpressions;

namespace BugZillaCrawler.UI
{
    public class Crawler
    {
        public static bool IsThisBugRelatedToOtherBug(int bugId, List<string> existingIds)
        {
            XmlDocument dom = GetBugInfo(bugId);

            XmlNodeList nodes = dom.GetElementsByTagName("dependson");

            if (nodes.Count > 0)
            {
                existingIds.Add(nodes[0].InnerText);
                return true;
            }
            else
            {
                return false;
            }
        }


        public static DateTime DownloadBugCreatingTime(int bugId)
        {
            DateTime time = DateTime.MinValue;

            XmlDocument dom = GetBugInfo(bugId);

            XmlNodeList nodes = dom.GetElementsByTagName("creation_ts");
            if (nodes.Count > 0)
            {
                string creationTime = nodes[0].InnerText;

                time = DateTime.Parse(creationTime.Replace(" PST", "").Replace(" PDT", ""));
            }

            return time;
        }


        public static string DownloadBugDescription(int bugId)
        {
            XmlDocument dom = GetBugInfo(bugId);

            XmlNodeList nodes = dom.GetElementsByTagName("long_desc");
            if (nodes.Count > 0)
            {
                XmlNodeList textNodes = (nodes[0] as XmlElement).GetElementsByTagName("thetext");
                if (textNodes.Count > 0)
                    return textNodes[0].InnerText;
                else
                    return string.Empty;
            }
            else
                return string.Empty;
        }

        private static XmlDocument GetBugInfo(int bugId)
        {
            string url = string.Format("https://bugzilla.mozilla.org/show_bug.cgi?ctype=xml&id={0}", bugId);

            // Download the content
            WebClient webClient = new WebClient();
            string content = webClient.DownloadString(url);
            
            XmlDocument dom = new XmlDocument();
            dom.LoadXml(content);
            return dom;
        }

        public static void DownloadBugHistory(int bugId)
        {
            string url = string.Format("https://bugzilla.mozilla.org/show_activity.cgi?id={0}", bugId);

            WebClient webClient = new WebClient();
            string content = webClient.DownloadString(url);

            //string content = File.ReadAllText("c.html");
            content = CleanAndFixHtml(content);

            XmlDocument dom = new XmlDocument();
            dom.LoadXml(content);

            XmlNodeList nodes = dom.GetElementsByTagName("table");
            if (nodes.Count > 1)
            {
                // We know that the 2nd table contains the history data

                XmlElement historyTable = nodes[1] as XmlElement;
                XmlNodeList rows = historyTable.GetElementsByTagName("tr");

                bool isFirst = true;
                BugHistory lastHistoryObj = null;
                foreach (XmlNode row in rows)
                {
                    if (isFirst)
                    {
                        // Skip the first row, its the header title
                        isFirst = false;
                        continue;
                    }

                    XmlNodeList columns = (row as XmlElement).GetElementsByTagName("td");

                    if (columns.Count == 5)
                    {
                        DateTime when = DateTime.Parse("1/1/1990");

                        if (!string.IsNullOrEmpty(columns[1].InnerText))
                            when = DateTime.Parse(columns[1].InnerText.Replace(" PST", "").Replace(" PDT", ""));
                        BugHistory history = DataAccess.Database.BugHistories.SingleOrDefault(h => h.When == when);
                        if (history == null)
                        {
                            history = new BugHistory();

                            history.BugId = bugId;
                            history.Who = columns[0].InnerText;
                            history.When = when;
                            history.What = columns[2].InnerText;
                            history.Removed = columns[3].InnerText;
                            history.Added = columns[4].InnerText;

                            DataAccess.Database.BugHistories.InsertOnSubmit(history);
                        }
                        lastHistoryObj = history;
                    }

                    if (columns.Count == 3 && lastHistoryObj != null)
                    {
                        lastHistoryObj.What += ", " + columns[0].InnerText;
                        lastHistoryObj.Removed += ", " + columns[1].InnerText;
                        lastHistoryObj.Added += ", " + columns[2].InnerText;
                    }
                }
                DataAccess.Database.SubmitChanges();
            }
        }

        public static void DownloadBugComment(int bugId)
        {
            string url = string.Format("https://bugzilla.mozilla.org/show_bug.cgi?id={0}", bugId);

            WebClient webClient = new WebClient();
            string content = webClient.DownloadString(url);

            //string content = File.ReadAllText("c.html");
            content = CleanAndFixHtml(content);

            XmlDocument dom = new XmlDocument();
            dom.LoadXml(content);

            List<string> data = GetNodeContent(dom, "div", "class", "bz_comment");

            foreach (string d in data)
            {
                string hash = EntLibHelper.GetMD5Hash(d);
                BugComment existing = DataAccess.Database.BugComments.SingleOrDefault(bugDes => bugDes.Hash == hash);

                if (existing == null)
                {
                    BugComment newDescription = new BugComment() { BugId = bugId, Description = d, Hash = hash, Time = FindDateTimeValue(d) };
                    DataAccess.Database.BugComments.InsertOnSubmit(newDescription);
                }
            }

            DataAccess.Database.SubmitChanges();
        }

        static Regex dateTimeFinderRgEx = new Regex(@"\d{4}-\d{2}-\d{2}\s*\d{2}:\d{2}:\d{2}");
        public static DateTime FindDateTimeValue(string content)
        {
            Match match = dateTimeFinderRgEx.Match(content);

            if (match.Success)
            {
                return DateTime.Parse(match.Value);
            }
            else
            {
                return DateTime.Parse("1/1/1753 12:00:00 AM");
            }
        }

        /// <summary>
        /// Returns the content of 
        /// </summary>
        /// <param name="dom"></param>
        /// <param name="nodeName">Node name</param>
        /// <param name="tokens">Node attributes (i.e. key1,value1, key2,value2)</param>
        /// <returns></returns>
        public static List<string> GetNodeContent(XmlDocument dom, string nodeName, params string[] tokens)
        {
            List<string> data = new List<string>();

            XmlNodeList nodes = dom.GetElementsByTagName(nodeName);

            foreach (XmlNode node in nodes)
            {
                if (node is XmlElement)
                {
                    XmlElement element = node as XmlElement;

                    if (element.MatchAttributes(tokens))
                        data.Add(element.InnerXml);
                }
            }

            return data;
        }

        /// <summary>
        /// Uses the external tidy.exe tool to fix any HTML/XML syntex 
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        private static string CleanAndFixHtml(string content)
        {
            if (File.Exists("input.html"))
                File.Delete("input.html");

            File.WriteAllText("input.html", content);

            ProcessStartInfo startInfo = new ProcessStartInfo("tidy.exe");
            startInfo.CreateNoWindow = false;
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            startInfo.Arguments = "-output output.html -asxml input.html";

            Process p = Process.Start(startInfo);
            p.WaitForExit();

            string xhtml = File.ReadAllText("output.html");
            if (File.Exists("output.html"))
                File.Delete("output.html");


            return xhtml;
        }
    }
}
